package personal.lijun.skeleton.springcloud.gateway;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;

/**
 * <p>
 * <b>Class name</b>: ApiTest
 * </p>
 * <p>
 * <b>Class description</b>: Class description goes here.
 * </p>
 * <p>
 * <b>Author</b>: Jiangdr
 * <b>Date </b>:  2019-06-30
 * </p>
 * <b>Change History</b>:<br/>
 * <p>
 * <p>
 * <pre>
 * Date          Author       Revision     Comments
 * ----------    ----------   --------     ------------------
 * 2019-06-30    Jiangdr       1.0          Initial Creation
 *
 * </pre>
 */
@RequestMapping("/api")
@RestController
public class ApiTest {

    @Autowired
    private PersonRepository repository;

    @GetMapping("/demo")
    public Mono<String>  demo(WebSession session){
//        Mono<User> userMono =testContext();
////        userMono.doOnNext(user -> {
////            System.out.println(user.getUsername());
////        }).thenEmpty(Mono.empty());
//////        userMono.subscribe(user -> {
//////            System.out.println(user);
//////        });
////        repository.callUserNext(userMono);
        return Mono.justOrEmpty("demo");
    }

    @GetMapping(value="testContext")
    public Mono<String> testContext(WebSession session) {
        return ReactiveSecurityContextHolder.getContext()
                .filter(c -> c.getAuthentication() != null)
                .map(SecurityContext::getAuthentication)
                .map(Authentication::getPrincipal)
                .cast(UserDetails.class) // 自定义的Context
                .map(UserDetails::getUsername);
    }
//    public Mono<User> testContext() {
////        String userName;
////        Mono<User> userMono = ReactiveSecurityContextHolder.getContext()
////                .filter(c -> c.getAuthentication() != null)
////                .map(SecurityContext::getAuthentication)
////                .map(Authentication::getPrincipal)
////                .map(user->{
////                    this.userName = ((User)user).getUsername();
////                    return user;
////                })
////                .cast(User.class);
//
////        userMono.doOnNext(user -> {
////            System.out.println(user.getUsername());
////        }).thenEmpty(Mono.empty());
////        return userMono;
//        return  getCurrentUser();
//    }

    private String userName;

    private Mono<User> getCurrentUser() {
        return ReactiveSecurityContextHolder.getContext()
                .switchIfEmpty(Mono.error(new IllegalStateException("ReactiveSecurityContext is empty")))
                .map(SecurityContext::getAuthentication)
                .map(Authentication::getPrincipal)
                .cast(User.class);
    }

    @GetMapping("/person/{id}")
    public Mono<ServerResponse> getPerson(ServerRequest request) {
        int personId = Integer.valueOf(request.pathVariable("id"));
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();
        Mono<Person> personMono = this.repository.getPerson(personId);
        return personMono
                .flatMap(person -> ServerResponse.ok().contentType(APPLICATION_JSON).body(fromObject(person)))
                .switchIfEmpty(notFound);
    }

    @PostMapping(value="person")
    public Mono<Void> createPerson(@RequestBody Person person) {

//        Mono<Person> person = request.bodyToMono(Person.class);
        Mono<Person> person2 =Mono.just(person);
        System.out.println("createPerson===========================================================");
        System.out.println(Thread.currentThread().getId());
        return this.repository.savePerson(person2);
    }

    public Mono<ServerResponse> listPeople(ServerRequest request) {
        Flux<Person> people = this.repository.allPeople();
        return ServerResponse.ok().contentType(APPLICATION_JSON).body(people, Person.class);
    }
}